package jdbc_test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/*
    JDBC连接数据库：
        是官方（sum公司）定义的所有关系型数据库可以共同使用的一套规则（也就是接口），各个数据库厂家分别去实现自己的数据库，
        将自己实现的类放到jar包中，将不同数据库的jar包导入到项目中，去实现jdbc的接口，本质是执行的实现类中的数据，子类实现接口
        ，多态机制

     快速入门：1.导入驱动jar包（不同数据库都有自己的jar包）
                    复制jar包到项目的bin（一般定义为libs）目录下，然后bin目录下右键选择：add as library
              2.注册驱动
              3.获取数据库连接对象Connection
              4.定义sql语句（对数据库进行操作）
              5.获取执行sql的对象Statement
              6.执行sql（达到修改效果）
              7.处理结果（查看是否执行成功）
              8.释放资源

     详解各个对象：
        1.DriverManager:驱动管理对象
            功能：1.注册驱动
                      类中方法： static void registerDriver(Driver driver) 注册与给定的驱动程序 DriverManager
                      写代码的时候用：Class.forName("com.mysql.cj.jdbc.Driver")
                        原因:通过看源码，在com.mysql.cj.jdbc.Driver这个类中存在静态代码块，用来注册驱动
                        因此，用Class.forName("com.mysql.cj.jdbc.Driver")更为简单

                        注意：mysql5之后的可以不写注册驱动，因为jar包中自动注册驱动，最好还是写

                 2.获取数据库连接
                      方法：static Connection getConnection(String url, String user, String password) 尝试建立与给定数据库URL的连接。
                        参数：url：指定连接的路径
                                    语法（不同数据库有不同的语法）：jdbc:mysql://ip地址或者域名:端口号/数据库名称?serverTimezone=UTC（世界统一时间，解决时间差）或者?serverTimezone=Asia/Shanghai
                             user：用户名
                             password:密码
                      细节：如果连接的是本机的mysql服务器，并且mysql服务器默认端口号为3306，则url可以写为：jdbc:mysql:///数据库名称
                           localhost表示：本机IP
        2.Connection:数据库连接对象
            功能：1.获取执行sql 的对象：
                        接口的方法：Statement createStatement() 创建一个 Statement对象，用于将SQL语句发送到数据库。
                                  PreparedStatement prepareStatement(String sql) 创建一个 PreparedStatement对象，用于将参数化的SQL语句发送到数据库。
                 2.管理事务：
                        开启事务：void setAutoCommit(boolean autoCommit) 将此连接的自动提交模式设置为给定状态。 调用该方法将参数设置为false，即开启事务（关闭自动提交，因此转为手动提交）
                        提交事务：void commit() 使自上次提交/回滚以来所做的所有更改都将永久性，并释放此 Connection对象当前持有的任何数据库锁。
                        回滚事务：void rollback() 撤消在当前事务中所做的所有更改，并释放此 Connection对象当前持有的任何数据库锁。
        3.Statement:执行sql的对象
              执行sql的方法：
                1.boolean  execute(String sql) 执行给定的SQL语句，这可能会返回多个结果。 （了解）
                2.int  executeUpdate(String sql) 执行给定的SQL语句，这可能是 INSERT ， UPDATE ，或 DELETE语句，或者不返回任何内容，如SQL DDL（create，update，delete）语句的SQL语句。
                       返回值int：统计影响（被修改）的行数，可以通过影响的行数判断DML语言是否执行成功，返回值>0，则执行成功，否侧执行失败
                3. ResultSet  executeQuery(String sql) 执行给定的SQL（select）语句，该语句返回单个 ResultSet对象(表单)。

        4.ResultSet:结果集对象（查询数据库的结果集（表单））
            接口中的方法：1.boolean  next() ：将光标从当前位置向下移动一行（每次只移动一行），判断当前表单行是否为最后一行，如果是返回false，如果不是返回true （获取的表单时游标（相当于指针）指向该表单的属性行，所以需要向下移动一行来读取属性中的数据0）
                        2.Xxx  getXxx(参数)：获取光标指向的当前行中的某个属性列
                            Xxx:代表要获取的某行中的某个属性列中数据的类型  eg：int getInt()获取int类型的数据
                            参数：1.int：代表列的编号，从1开始，一次只能获取一行中的指定的一列
                                 2.String：代表列的属性名称，当列是很多列的时候，直接通过列的属性名去找
            上述方法只能读取查询表单中的一行，若要查询所有表单行，需要使用循环，结束条件为 next() 为没有数据（当前行为末尾行，下一行不再有数据）

        5.PreparedStatement:执行sql的对象
            解决SQL注入问题：在拼接sql时，有一些sql的特殊关键字参与字符串的拼接，会造成安全问题
                1.用户随便输入账号，密码为：'a' or 'a' = 'a'， SQL语句为：select * from student where username = 'fjlasflkajd' and password = 'a' or 'a' = 'a',
                                                                    本来是需要用户名和密码都要正确才可以查询成功，但是该查询语句的where后面的结果为true，也可查询，
                                                                    从而造成安全问题

                2.解决sql注入问题：使用PreparedStatement接口
                3.PreparedStatement使用的是预编译sql语句，比Statement使用的静态sql语句更安全
                    预编译sql语句：账号密码的参数使用?作为占位符

                4.步骤：1.导入驱动jar包（不同数据库都有自己的jar包）
                         复制jar包到项目的bin（一般定义为libs）目录下，然后bin目录下右键选择：add as library
                       2.注册驱动
                       3.获取数据库连接对象Connection
                       4.定义sql语句（对数据库进行操作）
                            注意：定义sql语句时（对于增删改的处理等），使用?作为占位符  如：select * from student where username = ? and password = ?;
                       5.获取执行sql的对象 PreparedStatement   Connection(Connection类型的变量).prepareStatement(String sql) 创建一个 PreparedStatement对象，用于将参数化的SQL语句发送到数据库
                       6.给?进行赋值
                            方法：setXxx(参数1，参数2)
                                    参数1：?的位置，从1开始
                                    参数2：?的值（赋值）
                       7.执行sql（达到修改效果）
                            注意：方法中不再需要传入参数，因为第5步已经传入sql参数了
                       8.处理结果（查看是否执行成功）
                       9.释放资源

                5.后去都会使用PreparedStatement对象来完成增删改查的所有操作
                    1.可以防止sql的注入
                    2.效率更高
 */
public class PracticeMain {
    public static void main(String[] args) {
        //1.导入驱动jar包
        Connection connection = null;
        Statement statement = null;
        try {
            //2.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");//将字节码文件加载到内存中
            //3.获取数据库连接对象
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/uuuu?serverTimezone=UTC", "root", "root");
            // 4.定义sql语句
            String sql = "update student set aaa = 60 where bbb = 'wang'";
            // 5.获取执行sql的对象
            statement = connection.createStatement();
            // 6.执行sql
            long l = statement.executeLargeUpdate(sql);
            //7.处理结果
            System.out.println(l);


        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //8.释放资源
            /*
                1.提升statement，connection作用域
                2.避免空指针异常，若第3步执行出错，后面的代码就执行不到
             */
            if(statement != null){
                try {
                    statement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(connection != null){
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }

        }
    }
}
